/*
* Copyright (c) 2020 AIIT XUOS Lab
* XiUOS is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*        http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

/**
 * 正常流程测试
*/

#include "../include/amp.h"

#if PLATFORM_CHOICE == PLATFORM_LINUX_USER

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include "../include/amp.h"

/* 测试任务集合 */
#define MAX_TASK_CNT (10U)
pthread_t threads[MAX_TASK_CNT] = { 0 };
char *dir_names[MAX_TASK_CNT] = { NULL }; /* 传递给每个任务线程的上级目录信息 */

#define CONVERT_TIME (1000 * 1000) /* 1S = 1000 * 1000 US */

/* /usr/amp_test/amp_test_[测试次数]/threads_[测试线程数]/result_[线程ID].txt*/
#define AMP_TIMES_DIR "/usr/amp_test/amp_test_%u/" /* 第几次测试 */
#define AMP_THREADS_DIR "threads_%u/" /* 本次测试有几个测试线程 */
#define AMP_RESULT_FILE "result_%u.txt" /* 本次测试中某一特定线程的测试结果 */

struct Payload {
    uint32_t msg_data[0]; /* 消息本体：负载放置在共享内存中 */
};

struct PayloadInfo {

    /* 测试次数 */
    uint32_t test_max;
    uint32_t test_cur;

    /* 通信任务线程数量 */
    const uint32_t thread_max;
    const uint32_t thread_min;
    const uint32_t thread_stp;
    uint32_t thread_cur;

    /* 通信任务次数 */
    const uint32_t times_max;
    const uint32_t times_min;
    const uint32_t times_stp;

    /* 通信任务数据量 */
    const uint32_t cnt_max;
    const uint32_t cnt_min;
    const uint32_t cnt_stp;
};

struct PayloadInfo payload_info =
{
    .test_max = 10, /* 测试10次 */
    // .test_max = 1, /* 测试10次 */
    .test_cur = 1,

    .thread_max = MAX_TASK_CNT, /* 最多10个测试线程 */
    // .thread_max = 1, /* 最多10个测试线程 */
    .thread_min = 1,
    .thread_stp = 1, /* 等差数列等差 */
    .thread_cur = 1,

    .times_max = 128 * KB, /* 十万次 */
    // .times_max = 4, /* 十万次 */
    .times_min = 1, /* 1 2 4 8 16 32 64 128 256 512 1 KB 2 4 8 16 32 64 128 KB : 18 轮 */
    .times_stp = 2, /* 等比数列比例 */

    .cnt_max = 64, /* 64个整形数据, 256B */
    // .cnt_max = 4, /* 64个整形数据, 256B */
    .cnt_min = 1, /* 1 2 4 8 16 32 64 ： 7 轮 */
    .cnt_stp = 2,  /* 等比数列等比 */
};

void* test_task(void* dir_name)
{
    char str_buffer[1024] = { 0 }; /* 字符串缓冲区 */
    pthread_t self_thread_id = pthread_self(); /* 当前线程ID */

    /* 由主任务传入给线程，且该目录已经被创建：/usr/amp_test/amp_test_[测试次数]/threads_[测试线程数]/*/
    char* dir = (char*)dir_name;

    char result_file_name[512] = { 0 }; /* 构建测试文件名称 */
    sprintf(result_file_name,AMP_RESULT_FILE,self_thread_id);

    strcat(str_buffer, dir);
    strcat(str_buffer, result_file_name);

    /* 创建测试文件 */
    int result_file_fd = open(str_buffer,O_RDWR | O_CREAT | O_APPEND,0600);
    if (result_file_fd < 0)
    {
        printf("thread_%u: open file fail = %s\n",self_thread_id, str_buffer);
        return NULL;
    }
    printf("thread_%u: open file success = %s\n",self_thread_id, str_buffer);

    /* 写入测试文件的文件头 */
    char* file_header = "send_times data_cnt cost_us\n";
    if (write(result_file_fd,file_header,strlen(file_header)) != strlen(file_header))
    {
        printf("thread-%u: write file header fail = %s\n",self_thread_id,str_buffer);
        return NULL;
    }
    fsync(result_file_fd); /* 刷新文件 */

    /* 开始初始化核间通信相关结构 */
	struct Client amp_client = { 0 };

    /* 初始化客户端：连接Linux核心 */
    if (client_ops.client_init(&amp_client, CORE_RTOS_ID) != 0)
    {
        printf("thread-%u: client init [target_channel = %u] fail\n",self_thread_id,CORE_RTOS_ID);
        return NULL;
    }
    printf("thread-%u: client init [target_channel = %u] success\n",self_thread_id,CORE_RTOS_ID);

    /* 从通道中拿取一个发送给回显服务的长期消息缓冲区 */
    struct Msg *msg = client_ops.empty_msg_get(&amp_client,RTOS_SERVICE_FLIP_ID);
    if (msg == NULL)
    {
        printf("thread-%u: empty msg get [service_id = %u] fail\n",self_thread_id,RTOS_SERVICE_FLIP_ID);
        return NULL;
    }
    printf("thread-%u: empty msg get [service_id = %u] success\n",self_thread_id,RTOS_SERVICE_FLIP_ID);

    struct Payload* payload = client_ops.shm_malloc(&amp_client, sizeof(uint32_t) * payload_info.cnt_max,MALLOC_TYPE_V); /* 开辟一块共享内存 */
    msg->offset = shm_ops.addr_to_offset(payload); /* 计算地址偏移量 */

    char* item_format = "%-10u %-8u %-12ld\n"; /* 测试结果的格式 */

    uint32_t cur_times = payload_info.times_min; /* 开始进行核间通信测试 */

    struct timeval time_start = { 0 };
    struct timeval time_end = { 0 };
    
    for (; cur_times <= payload_info.times_max; ) /* 发送次数循环 */
    {
        /* 发送数据量循环 */
        uint32_t cur_cnt = payload_info.cnt_min;

        for (; cur_cnt <= payload_info.cnt_max; )
        {
            msg->length = cur_cnt; /* 消息携带的个数 */
            
            gettimeofday(&time_start,NULL); /* 开始计时 */

            for (int i = 0; i < cur_times; i++) /* 具体发送任务 */
            {
                for (int j = 0; j < cur_cnt; j++) /* 数据填充 */
                {
                    payload->msg_data[j] = i;
                }

                if (client_ops.msg_send_and_notify(&amp_client,msg) != 0) /* 发送消息并通知对方 */
                {
                    printf("thread-%u: msg send fail\n",self_thread_id);
                    return NULL;
                }

                while (client_ops.msg_poll(msg) != 0) /* 等待消息响应 */
                {
                    ;
                }

                for (int j = 0; j < cur_cnt; j++) /* 校验响应数据 */
                {
                    if (payload->msg_data[j] != ~i)
                    {
                        printf("thread-%u: msg response fail\n",self_thread_id);
                        return NULL;
                    }
                }
                
                /* 重置该消息状态 */
                msg->flag.deal_state = MSG_DEAL_STATE_NO;
            }
            
            gettimeofday(&time_end,NULL); /* 结束计时 */

            /* 将测试结果写入到文件中 */
            memset(str_buffer,0,1024);

            sprintf(str_buffer,item_format, cur_times, cur_cnt, (time_end.tv_sec * CONVERT_TIME + time_end.tv_usec) - (time_start.tv_sec * CONVERT_TIME + time_start.tv_usec));

            if (write(result_file_fd,str_buffer,strlen(str_buffer)) != strlen(str_buffer))
            {
                printf("thread-%u: write result item fail\n",self_thread_id);
                return NULL;
            }
            fsync(result_file_fd); /* 刷新文件 */
            
            printf("thread-%u: %s",self_thread_id, str_buffer);

            /* 更新发送数量 */
            cur_cnt *= payload_info.cnt_stp;
        }
        
        /* 更新发送次数 */
        cur_times *= payload_info.times_stp;
    }
    
    client_ops.empty_msg_put(&amp_client,msg); /* 归还这个消息体 */
    client_ops.shm_free(&amp_client,payload); /* 归还申请的共享内存 */
    client_ops.client_destory(&amp_client); /* 销毁交互体 */

    /* 关闭测试文件 */
    close(result_file_fd);

    printf("thread-%u: test over\n",self_thread_id);

    return NULL;
}

/**
 * arg1: test_times 需要循环测试的次数
*/
int main(int argc, char *argv[])
{
    uint32_t arg_test_times = atoi(argv[1]);

    payload_info.test_max = arg_test_times;

    printf("test times this app = %u\n",arg_test_times);

    /* 初始化通道信息和共享内存 */
    channel_ops.channels_init();
    shm_ops.shm_init();

    for (int i = 0; i < MAX_TASK_CNT; i++)
    {
        char* dir_name = malloc(512);
        dir_names[i] = dir_name;
    }
    
    char times_dir_buffer[512] = { 0 };
    char tasks_dir_buffer[512] = { 0 };

    for (; payload_info.test_cur <= payload_info.test_max; payload_info.test_cur ++)
    {
        /* 创建目录：/usr/amp_test/amp_test_%u/ */
        memset(times_dir_buffer,0,512);
        sprintf(times_dir_buffer,AMP_TIMES_DIR,payload_info.test_cur);
        if (mkdir(times_dir_buffer,0666) < 0)
        {
            printf("create dir fail = %s\n",times_dir_buffer);
            return 0;
        }
        printf("create dir success = %s\n",times_dir_buffer);

        payload_info.thread_cur = payload_info.thread_min; /* 更新本次测试的线程数起始值 */
        for (; payload_info.thread_cur <= payload_info.thread_max; )
        {
            /* 创建目录：/usr/amp_test/amp_test_%u/threads_%u/ */
            memset(tasks_dir_buffer,0,512);
            memset(dir_names[payload_info.thread_cur - 1],0,512);

            sprintf(tasks_dir_buffer,AMP_THREADS_DIR,payload_info.thread_cur);
            strcat(dir_names[payload_info.thread_cur - 1],times_dir_buffer);
            strcat(dir_names[payload_info.thread_cur - 1],tasks_dir_buffer);

            if (mkdir(dir_names[payload_info.thread_cur - 1],0666) < 0)
            {
                printf("create dir fail = %s\n",dir_names[payload_info.thread_cur - 1]);
                return 0;
            }
            printf("create dir success = %s\n",dir_names[payload_info.thread_cur - 1]);

            /* 创建通信任务线程 */
            for (int i = 0; i < payload_info.thread_cur; i++)
            {
                if (pthread_create(&threads[i],NULL,test_task,(void*)dir_names[payload_info.thread_cur - 1]) != 0)
                {
                    printf("create task fail\n");
                    return 0;
                }
            }
            
            /* 等待通信任务线程结束 */
            for (int i = 0; i < payload_info.thread_cur; i++)
            {
                pthread_join(threads[i],NULL);
            }

            payload_info.thread_cur += payload_info.thread_stp; /* 更新下一轮测试*/
        }
    }
    
    printf("all test over\n");

    return 0;
}

#endif /* PLATFORM_CHOICE == PLATFORM_LINUX_USER */